/*  Implementation of Unix/Linux tail command.
 * 	Output the last part of files. 
 * 	With more than one FILE, precede each with a header giving the file  name.
 *  With no FILE, or when FILE is -, read standard input.

    Copyright (C) 2012  Vasil Kostov <noobed>
    For any questions or advices don't hesitate to contact me - noobed7@gmail.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

 */

package tail;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;

/**
 * This class handles the arguments that have been passed. When a user creates a
 * new instance he passes the arguments to the constructor. Afterwards a method
 * run() is automatically called to fire the needed tail instructions.
 * 
 * @author Vasil Kostov <noobed>
 * @author noobed7@gmail.com
 * @version 1.0 BETA
 */
public class Tail {

	private boolean linesOption;
	private boolean bytesOption;
	private boolean startFromEnd;
	private boolean follow;

	// argumentCount contains value of argument -n or -c if used
	private int argumentCount;

	private String[] listOfFiles;
	private int numberOfFiles = 0;

	private TailCommand tailcomm = new TailCommand();

	/**
	 * 
	 * @param args
	 *            Should contain all the arguments passed to tail - i.e. file
	 *            names, options.
	 * @throws IOException
	 *             If an I/O error occurs.S
	 * 
	 */
	public Tail(String[] args) throws IOException {
		//shutdown hook test!
	//to be continue...
		
		
		// if there're no arguments - read from stdin
		if (args.length == 0) {
			tailcomm.readLastLinesFromConsole(10, new BufferedReader(
					new InputStreamReader(System.in)));
		} else {
			listOfFiles = new String[args.length];
			for (int i = 0; i < args.length; i++) {
				if (args[i].equals("--follow") || args[i].equals("-f"))
					follow = true;

				else if (args[i].equals("--lines") || args[i].equals("-n"))
					if (i < args.length - 1) {
						bytesOption = false;
						linesOption = true;

						// after an option is present the next argument should
						// be number
						// for instance: -n +23, -n -23, -n 23 (all these are
						// eligible)
						try {
							calculateArgumentNumber(args[i + 1]);
						} catch (NumberFormatException e) {
							System.out.println("tail:"
									+ e.getMessage().substring(17) // this is
																	// the
																	// file's
																	// name in
																	// brackets
									+ " invalid number of lines");
						}
						i++;
					}
					// tail: option requires an argument -- 'n'
					// Try `tail --help' for more information.
					else {
						System.out
								.println("tail: option requires an argument -- 'n'\n"
										+ "Try `tail --help' for more information.");
						System.exit(0);
					}

				else if (args[i].equals("--bytes") || args[i].equals("-c"))
					if (i < args.length - 1) {
						bytesOption = true;
						linesOption = false;

						try {
							calculateArgumentNumber(args[i + 1]);
						} catch (NumberFormatException e) {
							System.out.println("tail:"
									+ e.getMessage().substring(17)
									+ "invalid number of bytes");
						}
						i++;
					} else {
						System.out
								.println("tail: option requires an argument -- 'c'\n"
										+ "Try `tail --help' for more information.");
						System.exit(0);
					}

				else
					listOfFiles[numberOfFiles++] = args[i];
			}
			// after setting the options and files
			// run does the actual printing
			run();
		}
	}

	/*
	 * The string is being converted into a number. (i.e. : +123, -234, 234 are
	 * all correct)
	 * 
	 * If the number is in wrong format an exception is thrown. (i.e. : +-345 is
	 * wrong)
	 */
	private void calculateArgumentNumber(String tmp)
			throws NumberFormatException {
		if (tmp.charAt(0) == '+') {
			startFromEnd = false;
			String str = "";
			for (int i = 1; i < tmp.length(); i++)
				str += tmp.charAt(i);
			argumentCount = Integer.parseInt(str);
		} else {
			argumentCount = Math.abs(Integer.parseInt(tmp));
			startFromEnd = true;
		}
	}

	/*
	 * This method distribute the work: If there's a follow option - everything
	 * is passed to TailCommand and the rest of the work is done there.
	 * Following stdin is disabled. Otherwise checking is done here and the
	 * accurate command is launched for all of the files.
	 * 
	 * IOException is thrown if an I/O error occurs.
	 */
	private void run() throws IOException {
		if (follow) {
			if (numberOfFiles == 0) {
				System.out
						.println("Tail: following standart input is ineffective!\n "
								+ "This option has been disabled!");
				System.exit(0);
			}
			tailcomm.tailForever(listOfFiles, numberOfFiles, linesOption,
					bytesOption, argumentCount, startFromEnd);
		} else {
			String pathname = "";
			File file = null;
			RandomAccessFile raf = null;

			int qq = 0;
			if (bytesOption == false && linesOption == false)
				qq = 1;
			if (bytesOption)
				qq = 2;
			if (linesOption)
				qq = 3;

			switch (qq) {
			case 1:
				if (numberOfFiles > 1) {
					System.out.println("==> " + listOfFiles[0] + " <==");
					for (int i = 0; i < numberOfFiles; i++) {
						pathname = listOfFiles[i];

						if (i > 0)
							System.out.println("\n==> " + pathname + " <==");
						file = new File(pathname);
						if (file.canRead()) {
							try {
								try {
									raf = new RandomAccessFile(file, "r");
									tailcomm.readLastLines(10, raf);
								} finally {
									if (raf != null)
										raf.close();
								}
							} catch (FileNotFoundException e) {
								System.out.println(e.getMessage());
							} catch (IOException e) {
								System.out.println(e.getMessage());
							}
						}
					}
				} else
					tailcomm.readLastLinesFromConsole(10, new BufferedReader(
							new InputStreamReader(System.in)));
				break;

			case 2:
				if (numberOfFiles > 1) {
					System.out.println("==> " + listOfFiles[0] + " <==");
					for (int i = 0; i < numberOfFiles; i++) {
						pathname = listOfFiles[i];

						if (i > 0)
							System.out.println("\n==> " + pathname + " <==");
						file = new File(pathname);
						if (file.canRead()) {
							try {
								try {
									raf = new RandomAccessFile(file, "r");
									if (startFromEnd)
										tailcomm.readLastBytes(argumentCount,
												raf);
									else
										tailcomm.readFromKthBytesToEnd(
												argumentCount, raf);
								} finally {
									if (raf != null)
										raf.close();
								}
							} catch (FileNotFoundException e) {
								System.out.println(e.getMessage());
							} catch (IOException e) {
								System.out.println(e.getMessage());
							}
						}
					}
				} else if (startFromEnd)
					tailcomm.readLastBytes(argumentCount, raf);
				else
					tailcomm.readFromKthByteConsole(
							argumentCount,
							new BufferedReader(new InputStreamReader(System.in)));
				break;

			case 3:
				if (numberOfFiles > 1) {
					System.out.println("==> " + listOfFiles[0] + " <==");
					for (int i = 0; i < numberOfFiles; i++) {
						pathname = listOfFiles[i];

						if (i > 0)
							System.out.println("\n==> " + pathname + " <==");
						file = new File(pathname);
						if (file.canRead()) {
							try {
								try {
									raf = new RandomAccessFile(file, "r");
									if (startFromEnd)
										tailcomm.readLastLines(argumentCount,
												raf);
									else
										tailcomm.readFromKthLineToEnd(
												argumentCount, raf);
								} finally {
									if (raf != null)
										raf.close();
								}
							} catch (FileNotFoundException e) {
								System.out.println(e.getMessage());
							} catch (IOException e) {
								System.out.println(e.getMessage());
							}
						}
					}
				} else if (startFromEnd)
					tailcomm.readLastLines(argumentCount, raf);
				else
					tailcomm.readFromKthLineConsole(
							argumentCount,
							new BufferedReader(new InputStreamReader(System.in)));
				break;
			}// switch end
		}
	}

	static {
		System.load("/home/vasil/Desktop/tailTest/libFINAL.so");
	}

}
